package cn.anecansaitin.firecrafting.common.crafting;

import cn.anecansaitin.firecrafting.ModConstant;
import cn.anecansaitin.firecrafting.api.client.crafting.IJEIRecipeRender;
import cn.anecansaitin.firecrafting.client.crafting.FireBlockRecipeRender;
import cn.anecansaitin.firecrafting.common.Config;
import cn.anecansaitin.firecrafting.common.util.ModJsonReader;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistryEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Map;

//产生方块的配方
public class FireBlockRecipe extends SimpleFireRecipe {
    private final ImmutableMap<Integer, Block> outputs;

    public FireBlockRecipe(ResourceLocation id, ImmutableList<Block> fireBlock, ImmutableMap<Integer, Block> outputs, NonNullList<Ingredient> inputs) {
        super(id, fireBlock, inputs);
        this.outputs = outputs;
    }

    @Override
    public IJEIRecipeRender getRender() {
        return FireBlockRecipeRender.INSTANCE;
    }

    //在配方正确时进行一系列行为
    @Override
    public boolean doAction(List<ItemStack> stacks, BlockPos pos, Level world) {
        //进行配方检测
        if (match(stacks, pos, world)) {
            //将产物尝试生成在世界上
            outputs.forEach((i, b) -> {
                //根据层数决定方块生成的位置（-1：火焰下方、0：火焰位置、1：火焰上方）
                BlockPos pos1 = switch (i) {
                    case -1 -> pos.below();
                    case 1 -> pos.above();
                    default -> pos;
                };
                //获取坐标位置方块的硬度
                float speed = world.getBlockState(pos1).getDestroySpeed(world, pos);
                //检测硬度（基岩为-1）、实体
                if (speed <= 3f && speed >= 0 && world.getBlockEntity(pos) == null) {
                    //放置方块
                    world.setBlock(pos1, b.defaultBlockState(), 2);
                }
            });

            return true;
        }
        return false;
    }

    public Map<Integer, Block> getOutputs() {
        return outputs;
    }

    //返回配方指定的序列化器
    @Override
    public @NotNull RecipeSerializer<?> getSerializer() {
        return ModRecipeTypes.FIRE_BLOCK_SERIALIZER.get();
    }

    //序列化器实现类
    public static class Serializer extends ForgeRegistryEntry<RecipeSerializer<?>> implements RecipeSerializer<FireBlockRecipe> {
        public static final String NAME = ModConstant.MOD_ID + ":fire_block_crafting";

        //从json转为配方
        @Override
        public @NotNull FireBlockRecipe fromJson(@NotNull ResourceLocation id, @NotNull JsonObject json) {
            //获取配方物品成分
            NonNullList<Ingredient> nonnulllist = ModJsonReader.ingredientsFromJson(GsonHelper.getAsJsonArray(json, "ingredients"));
            //判断配方成分的可用性
            if (nonnulllist.isEmpty()) {
                throw new JsonParseException("火焰燃烧(方块)配方内没有任何原料 No ingredients for fire block recipe");
            } else if (nonnulllist.size() > Config.WORLD_MAX_COUNT.get()) {
                throw new JsonParseException("火焰燃烧(方块)配方内的原料超出上限" + Config.WORLD_MAX_COUNT.get() + "  Too many ingredients for fire block recipe. The maximum is " + Config.WORLD_MAX_COUNT.get());
            }
            //获取配方方块产物
            ImmutableMap<Integer, Block> output = readBlocks(json);
            //判断方块是否可用
            if (output.isEmpty()) {
                throw new JsonParseException("火焰燃烧(方块)配方内没有任何产物 No output for fire block recipe");
            }
            //获取可用火焰
            ImmutableList<Block> fire = ImmutableList.copyOf(ModJsonReader.blocksFromJson(json, "fire"));
            //判断火焰可用性
            if (fire.isEmpty()) {
                throw new JsonParseException("火焰燃烧(方块)配方内没有指定火焰 No fire block for fire block recipe");
            }
            return new FireBlockRecipe(id, fire, output, nonnulllist);
        }

        //从json中读取方块以及层数
        private ImmutableMap<Integer, Block> readBlocks(JsonObject json) {
            //新建map构造器
            ImmutableMap.Builder<Integer, Block> builder = ImmutableMap.builder();
            //读取json元素“output”
            JsonArray jsonArray = json.getAsJsonArray("output");
            //遍历读取“output”中每个元素
            for (JsonElement output : jsonArray) {
                //element转为object
                JsonObject object = output.getAsJsonObject();
                //读取block
                String block = object.get("block").getAsString();
                //读取floor
                int floor = object.get("floor").getAsInt();
                //向map中放入方块和层数
                builder.put(floor, ForgeRegistries.BLOCKS.getValue(new ResourceLocation(block)));
            }
            //完成map构建
            ImmutableMap<Integer, Block> map = builder.build();
            //检测方块生成位置
            if (map.size() != jsonArray.size()) {
                throw new JsonParseException("火焰燃烧(方块)配方内有生成在相同位置的多个产物 More then one output set in same floor");
            }

            return map;
        }

        //从网络获取配方
        @Nullable
        @Override
        public FireBlockRecipe fromNetwork(@NotNull ResourceLocation id, FriendlyByteBuf buffer) {
            //获取火焰数量
            int fireCount = buffer.readVarInt();
            //创建list构造器
            ImmutableList.Builder<Block> fireBuilder = ImmutableList.builder();
            //从buffer中读取火焰
            for (int i = 0; i < fireCount; i++) {
                fireBuilder.add(ForgeRegistries.BLOCKS.getValue(buffer.readResourceLocation()));
            }
            //从buffer中读取方块产物map
            Map<Integer, Block> blockMap = buffer.readMap(FriendlyByteBuf::readVarInt, (b) -> ForgeRegistries.BLOCKS.getValue(b.readResourceLocation()));
            //将产物map转为不可修改map
            ImmutableMap<Integer, Block> outputs = ImmutableMap.copyOf(blockMap);
            //从buffer中获取配方成分数量并创建集合
            NonNullList<Ingredient> input = NonNullList.withSize(buffer.readVarInt(), Ingredient.EMPTY);
            //从buffer中获取指定数量成分
            for (int i = 0; i < input.size(); i++) {
                input.set(i, Ingredient.fromNetwork(buffer));
            }

            return new FireBlockRecipe(id, fireBuilder.build(), outputs, input);
        }

        //将配方传到网络
        @Override
        public void toNetwork(FriendlyByteBuf buffer, FireBlockRecipe recipe) {
            //获取配方火焰
            ImmutableList<Block> fireBlock = recipe.fireBlock;
            //向buffer写入火焰数量
            buffer.writeVarInt(fireBlock.size());
            //向buffer写入火焰
            fireBlock.forEach((b) -> buffer.writeResourceLocation(b.getRegistryName()));
            //获取配方产物
            ImmutableMap<Integer, Block> outputs = recipe.outputs;
            //将产物map写入buffer
            buffer.writeMap(outputs, FriendlyByteBuf::writeVarInt, (buf, b) -> buf.writeResourceLocation(b.getRegistryName()));
            //获取配方成分
            NonNullList<Ingredient> inputs = recipe.inputs;
            //向buffer写入成分数量
            buffer.writeVarInt(inputs.size());
            //向buffer写入成分
            inputs.forEach((i) -> i.toNetwork(buffer));
        }
    }
}